//
// Author: Vladimir Migashko <migashko@gmail.com>, (C) 2011
//
// Copyright: See COPYING file that comes with this distribution
//

#ifndef FASYS_FASYS_CHANNELS_OBSERVERS_CONTAINER_HPP
#define FASYS_FASYS_CHANNELS_OBSERVERS_CONTAINER_HPP

#include "types.hpp"
#include "tags.hpp"
#include "ichannel_observer.hpp"

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>

namespace fasys{

using namespace boost;
using namespace boost::multi_index;

struct channels_observer_info
{
  channel_id_t channel_id;
  ichannel_observer *observer;

  channels_observer_info()
    : channel_id(0)
    , observer(0) {}

  channels_observer_info(channel_id_t channel_id, ichannel_observer *observer)
    : channel_id(channel_id)
    , observer(observer)
  {}

};

struct channels_observer_ordered_by_channel_id
  : ordered_unique<
      tag<_channel_id_>,
      composite_key<
        channels_observer_info,
        member<channels_observer_info, channel_id_t, &channels_observer_info::channel_id>,
        member<channels_observer_info, ichannel_observer*, &channels_observer_info::observer>
      >
   >
{
};

struct channels_observer_ordered_by_observer
  : ordered_unique<
      tag<_observer_>,
      composite_key<
        channels_observer_info,
        member<channels_observer_info, ichannel_observer*, &channels_observer_info::observer>,
        member<channels_observer_info, channel_id_t, &channels_observer_info::channel_id>
      >
   >
{
};

/// //////////////////////////////////////////////////


struct channels_observer_indices
  : indexed_by<
      channels_observer_ordered_by_channel_id,
      channels_observer_ordered_by_observer
    >
{};

typedef multi_index_container<channels_observer_info, channels_observer_indices> channels_observer_multi_index;

class channels_observers_container
  : private channels_observer_multi_index
{
  typedef channels_observer_multi_index super;
  typedef super::index<_channel_id_>::type by_channel_id_type;
  typedef super::index<_observer_>::type by_observer_type;

public:

  channels_observers_container()
  {
  }

  void subscribe(channel_id_t channel_id, ichannel_observer *observer)
  {
    by_channel_id_type& by_channel_id = super::get<_channel_id_>();
    by_channel_id_type::iterator itr = by_channel_id.find( make_tuple(channel_id, observer) );
    if ( itr == by_channel_id.end() )
      super::insert( channels_observer_info(channel_id, observer) );
  }

  void describe(channel_id_t channel_id, ichannel_observer *observer)
  {
    by_channel_id_type& by_channel_id = super::get<_channel_id_>();
    by_channel_id_type::iterator itr = by_channel_id.find( make_tuple(channel_id, observer) );
    if ( itr != by_channel_id.end() )
      by_channel_id.erase(itr);

  }

  void describe(ichannel_observer *observer)
  {
    by_observer_type& by_observer = super::get<_observer_>();
    std::pair<
      by_observer_type::iterator,
      by_observer_type::iterator
    > range = by_observer.equal_range(observer);
    by_observer.erase(range.first, range.second);
  }

  template<typename F>
  void fire(channel_id_t channel_id, F f) const 
  {
    const by_channel_id_type& by_channel_id = super::get<_channel_id_>();
    std::pair<
      by_channel_id_type::const_iterator,
      by_channel_id_type::const_iterator
    > range = by_channel_id.equal_range(channel_id);

    std::for_each(range.first, range.second, f);
  }
private:
};

}

#endif

